package com.ltx.bugseeker.service.impl;

import com.ltx.bugseeker.common.ResultCode;
import com.ltx.bugseeker.dto.SendSmsDTO;
import com.ltx.bugseeker.exception.BusinessException;
import com.ltx.bugseeker.service.ISmsService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;
import java.util.concurrent.TimeUnit;

/**
 * 短信服务实现类
 * 提供短信验证码发送、验证等功能的具体实现
 *
 * @author ltx
 * @since 2025-01-26 16:30:00
 */
@Slf4j
@Service
public class SmsServiceImpl implements ISmsService {

    /**
     * 验证码长度
     */
    private static final int SMS_CODE_LENGTH = 6;
    /**
     * 验证码过期时间（分钟）
     */
    private static final int SMS_CODE_EXPIRE_MINUTES = 5;
    /**
     * 发送频率限制时间（秒）
     */
    private static final int SEND_FREQUENCY_LIMIT_SECONDS = 60;
    /**
     * 最大验证尝试次数
     */
    private static final int MAX_VERIFY_ATTEMPTS = 5;
    /**
     * Redis Key前缀
     */
    private static final String SMS_CODE_KEY_PREFIX = "sms:code:";
    private static final String SMS_SEND_FREQUENCY_KEY_PREFIX = "sms:frequency:";
    private static final String SMS_VERIFY_ATTEMPTS_KEY_PREFIX = "sms:attempts:";
    /**
     * 随机数生成器
     */
    private static final SecureRandom RANDOM = new SecureRandom();

    @Resource
    private RedissonClient redissonClient;

    /**
     * 发送短信验证码
     *
     * @param sendSmsDTO 发送短信请求参数
     * @return 是否发送成功
     * @throws Exception 发送失败时抛出异常
     */
    @Override
    public boolean sendSmsCode(SendSmsDTO sendSmsDTO) throws Exception {
        String phone = sendSmsDTO.getPhone();
        String type = sendSmsDTO.getType();

        log.info("开始发送短信验证码，手机号：{}，类型：{}", phone, type);

        try {
            // 检查发送频率限制
            if (!checkSendFrequency(phone)) {
                log.warn("发送短信验证码频率限制，手机号：{}", phone);
                throw new BusinessException(ResultCode.SMS_SEND_FREQUENCY_LIMIT);
            }

            // 生成6位随机验证码
            String smsCode = generateSmsCode();

            // 存储验证码到Redis，设置过期时间
            String codeKey = SMS_CODE_KEY_PREFIX + phone;
            RBucket<String> codeBucket = redissonClient.getBucket(codeKey);
            codeBucket.set(smsCode, SMS_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

            // 设置发送频率限制
            String frequencyKey = SMS_SEND_FREQUENCY_KEY_PREFIX + phone;
            RBucket<String> frequencyBucket = redissonClient.getBucket(frequencyKey);
            frequencyBucket.set("1", SEND_FREQUENCY_LIMIT_SECONDS, TimeUnit.SECONDS);

            // 重置验证尝试次数
            String attemptsKey = SMS_VERIFY_ATTEMPTS_KEY_PREFIX + phone;
            RBucket<Integer> attemptsBucket = redissonClient.getBucket(attemptsKey);
            attemptsBucket.delete();

            // 模拟发送短信（在控制台输出验证码）
            log.info("=== 短信验证码发送成功 ===");
            log.info("手机号：{}", phone);
            log.info("验证码：{}", smsCode);
            log.info("类型：{}", type);
            log.info("有效期：{}分钟", SMS_CODE_EXPIRE_MINUTES);
            log.info("========================");

            // 在实际项目中，这里应该调用第三方短信服务商的API
            // 例如：阿里云短信、腾讯云短信等

            log.info("短信验证码发送成功，手机号：{}", phone);
            return true;

        } catch (BusinessException e) {
            log.error("发送短信验证码业务异常，手机号：{}，错误信息：{}", phone, e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("发送短信验证码系统异常，手机号：{}", phone, e);
            throw new BusinessException(ResultCode.SMS_SEND_FAILED);
        }
    }

    /**
     * 验证短信验证码
     *
     * @param phone   手机号码
     * @param smsCode 验证码
     * @param type    验证码类型
     * @return 验证是否成功
     * @throws Exception 验证失败时抛出异常
     */
    @Override
    public boolean verifySmsCode(String phone, String smsCode, String type) throws Exception {
        log.info("开始验证短信验证码，手机号：{}，验证码：{}，类型：{}", phone, smsCode, type);

        try {
            // 检查验证尝试次数
            if (!checkVerifyAttempts(phone)) {
                log.warn("短信验证码尝试次数超限，手机号：{}", phone);
                throw new BusinessException(ResultCode.SMS_VERIFY_ATTEMPTS_EXCEEDED);
            }

            // 获取存储的验证码
            String codeKey = SMS_CODE_KEY_PREFIX + phone;
            RBucket<String> codeBucket = redissonClient.getBucket(codeKey);
            String storedCode = codeBucket.get();

            if (storedCode == null) {
                log.warn("短信验证码不存在或已过期，手机号：{}", phone);
                incrementVerifyAttempts(phone);
                throw new BusinessException(ResultCode.SMS_CODE_EXPIRED);
            }

            if (!storedCode.equals(smsCode)) {
                log.warn("短信验证码错误，手机号：{}，输入验证码：{}，正确验证码：{}", phone, smsCode, storedCode);
                incrementVerifyAttempts(phone);
                throw new BusinessException(ResultCode.SMS_CODE_INVALID);
            }

            // 验证成功，删除验证码和尝试次数记录
            codeBucket.delete();
            String attemptsKey = SMS_VERIFY_ATTEMPTS_KEY_PREFIX + phone;
            RBucket<Integer> attemptsBucket = redissonClient.getBucket(attemptsKey);
            attemptsBucket.delete();

            log.info("短信验证码验证成功，手机号：{}", phone);
            return true;

        } catch (BusinessException e) {
            log.error("验证短信验证码业务异常，手机号：{}，错误信息：{}", phone, e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("验证短信验证码系统异常，手机号：{}", phone, e);
            throw new BusinessException(ResultCode.SYSTEM_ERROR);
        }
    }

    /**
     * 检查发送频率限制
     *
     * @param phone 手机号码
     * @return 是否可以发送（true: 可以发送, false: 频率限制中）
     */
    @Override
    public boolean checkSendFrequency(String phone) {
        String frequencyKey = SMS_SEND_FREQUENCY_KEY_PREFIX + phone;
        RBucket<String> frequencyBucket = redissonClient.getBucket(frequencyKey);
        return !frequencyBucket.isExists();
    }

    /**
     * 检查验证码尝试次数
     *
     * @param phone 手机号码
     * @return 是否可以继续尝试（true: 可以尝试, false: 已达到最大尝试次数）
     */
    @Override
    public boolean checkVerifyAttempts(String phone) {
        String attemptsKey = SMS_VERIFY_ATTEMPTS_KEY_PREFIX + phone;
        RBucket<Integer> attemptsBucket = redissonClient.getBucket(attemptsKey);
        Integer attempts = attemptsBucket.get();
        return attempts == null || attempts < MAX_VERIFY_ATTEMPTS;
    }

    /**
     * 增加验证尝试次数
     *
     * @param phone 手机号码
     */
    private void incrementVerifyAttempts(String phone) {
        String attemptsKey = SMS_VERIFY_ATTEMPTS_KEY_PREFIX + phone;
        RBucket<Integer> attemptsBucket = redissonClient.getBucket(attemptsKey);
        Integer attempts = attemptsBucket.get();
        if (attempts == null) {
            attempts = 0;
        }
        attempts++;
        // 设置24小时过期时间
        attemptsBucket.set(attempts, 24, TimeUnit.HOURS);
        log.info("增加短信验证码尝试次数，手机号：{}，当前尝试次数：{}", phone, attempts);
    }

    /**
     * 生成6位随机数字验证码
     *
     * @return 验证码字符串
     */
    private String generateSmsCode() {
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < SMS_CODE_LENGTH; i++) {
            code.append(RANDOM.nextInt(10));
        }
        return code.toString();
    }
}